package com.example.controller;

import com.example.common.Result;
import com.example.common.UploadResult;
import com.example.model.bo.FileLog;
import com.example.model.bo.GetBigRecord;
import com.example.model.bo.SplitFileRecord;
import com.example.service.BigFileStoreService;
import com.example.service.FileStoreService;
import com.example.service.IpfsService;
import com.example.utils.SplitFileUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@Slf4j
@RestController
@CrossOrigin//支持跨域
public class FileUploadController {

    @Autowired
    IpfsService ipfsService;

    @Autowired
    private FileStoreService fileStoreService;

    @Autowired
    private BigFileStoreService bigFileStoreService;

    private final HashMap<String, Boolean> alreadyUploaded = new HashMap<>();

    /**
     * 拆分文件并上传到区块链
     * @param file 文件
     * @param size 拆分的基数
     * @return
     * @throws Exception
     */
    @PostMapping("/addbigfile")
    public Result upload(@RequestParam("file") MultipartFile file,
                         @RequestParam(value = "size", required = false, defaultValue = "2")int size) throws Exception {

        if(file == null){
            return Result.error("文件上传失败！");
        }
        if(file.isEmpty()){
            return Result.error("文件为空，上传失败！");
        }

        // 检查文件大小是否超过10M
        long maxFileSize = 10 * 1024 * 1024; // 10M
        if (file.getSize() > maxFileSize) {
            return Result.error("文件大小不能超过10M！");
        }

        //前端上传的文件的文件名
        String originalFilename = file.getOriginalFilename();
        if(bigFileStoreService.blockchainisexit(originalFilename)){
            return Result.error("文件重复，请重新上传！");
        }

        int index = originalFilename.lastIndexOf(".");
        //获取后缀名
        String suffixName = originalFilename.substring(index);
        //获取前缀名
        String preName = originalFilename.substring(0, index);

        // 确保前缀字符串至少有三个字符
        if (preName.length() < 3) {
            preName = String.format("%-3s", preName).replace(' ', 'x');
        }



        File tempFile = File.createTempFile(preName, suffixName);
        file.transferTo(tempFile);
        //前端上传的文件已经临时存储到本地
        ArrayList<File> fileList = SplitFileUtil.splitFile(tempFile, size);
        String dir = "/opt/demo/back/assets/";

        //返回数据列表
        ArrayList<UploadResult> resultList = new ArrayList<>();

        //将每个子文件上传到Ipfs
        for(int i = 0; i < fileList.size(); i++){
            //当前子文件
            File f = fileList.get(i);
            //将当前子文件上传至IPFS
            String hashData = ipfsService.uploadToIpfs(dir + f.getName());
            //创建当前子文件对应的实体类
            SplitFileRecord splitFileRecord = new SplitFileRecord();
            splitFileRecord.setOrderInFile((i + 1) + "");
            splitFileRecord.setCid(hashData);
            splitFileRecord.setFileName(originalFilename);
            splitFileRecord.setEncrypt_Cid("");
            splitFileRecord.setReceiverUserName("");

            //调用Service实现文件上链
            UploadResult uploadResult = fileStoreService.addSplitFile(splitFileRecord);
            //设置上传时间
            LocalDateTime now = LocalDateTime.now();
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            String time = now.format(formatter);
            uploadResult.setUploadTime(time);
            //设置文件名
            uploadResult.setFilename(originalFilename);
            resultList.add(uploadResult);
        }
        //数组：字块和其对应的哈希值
        String[] hashArr = new String[resultList.size()];
        for(int i = 0; i < resultList.size(); i++){
            hashArr[i] = resultList.get(i).getHash();
        }
        //调用Service上传 文件名和对应的哈希数组的映射 到区块链上
        fileStoreService.log(originalFilename, hashArr);
        //删除文件
        for(File f : fileList){
            f.delete();
        }

        //输出日志
        log.info("上传文件：{}", file.getOriginalFilename());

        //删除临时文件
        tempFile.deleteOnExit();
        return Result.success(resultList);
    }


    /**
     * 根据文件名查询返回拆分上传的文件
     * @param filename 文件名
     * @return
     * @throws Exception
     */
    @RequestMapping("/getbigfile")
    public ResponseEntity<FileSystemResource> downloadBigIpfs(String filename) throws Exception {
        //前端传入文件名，根据文件名（区块上的唯一）
        //1 根据文件名去区块查询，为了获得uid，以及顺序
        //2 按照顺序，根据uid去IPFS查询处每一个文件
        //3 拼接文件，发送给前端

        //1 根据文件名去区块查询，为了获得uid，以及顺序
        List<GetBigRecord> records = bigFileStoreService.getRecordsByUserNameAndFilename(filename);
        //2 按照顺序，根据uid去IPFS查询处每一个文件
        log.info("上传的文件信息:{}", records);
        String[]filePaths = new String[records.size()];
        String commonPath = "/opt/demo/back/assets/";
        for(int i=0; i< records.size(); i++){
            GetBigRecord record = records.get(i);
            ipfsService.downFromIpfs( record.getCid(),
                    commonPath+filename+String.valueOf(i) ); // 第二个参数需要填入地址?
            filePaths[i] = commonPath+filename+String.valueOf(i);
        }
        //3 拼接文件，发送给前端
        String desFilePath = commonPath+filename;
        byte[] buufer =  new byte[1024];
        try (BufferedOutputStream bw = new BufferedOutputStream(new FileOutputStream(desFilePath))) {
            for (String filePath : filePaths) {
                try (BufferedInputStream br = new BufferedInputStream(new FileInputStream(filePath))) {
                    int len = 0;
                    while ( (len = br.read(buufer)) != -1 ) {
                        bw.write(buufer, 0, len);
//                        bw.newLine(); // 确保写入新行，如果原始文件没有新行符
                    }
                }
            }
            log.info("Files merged successfully to:{} ", desFilePath);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //4 将文件返回给前端
        // 使用Paths来处理文件路径
        Path path = Paths.get(desFilePath);
        File file = path.toFile();
//        File file = new File(desFilePath);
        // 检查文件是否存在
        if (!Files.exists(path)) {
            return ResponseEntity.notFound().build();
        }

        // 如果文件不是可读的，也返回403或者其他错误
        if (!Files.isReadable(path)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
        }

        // 设置Content-Type为适当的MIME类型
        String contentType = "application/octet-stream";

        // 创建FileSystemResource实例
        FileSystemResource resource = new FileSystemResource(file);

        // 告诉浏览器这是一个附件，并提供文件名
        String fileName = file.getName();
        try {
            String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
            return ResponseEntity.ok()
                    .contentType(MediaType.parseMediaType(contentType))
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + encodedFileName + "\"")
                    .body(resource);
        } catch (UnsupportedEncodingException e) {
            // 处理编码异常
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    /**
     * 到区块链查询拆分上传历史记录
     * @return
     * @throws Exception
     */
    @CrossOrigin
    @GetMapping("/log")
    public Result log() throws Exception {
        //调用Service到区块链上查询历史上传记录
        List<FileLog> fileLogs= fileStoreService.list();
        return Result.success(fileLogs);
    }

    /**
     * 到区块链 根据文件名查询拆分上传的子区块的详细信息
     * @param fileName
     * @return
     * @throws Exception
     */
    @GetMapping("/details")
    public Result details(String fileName) throws Exception {
        //根据文件名去区块查询，为了获得uid，以及顺序
        List<GetBigRecord> records = bigFileStoreService.getRecordsByUserNameAndFilename(fileName);
        return Result.success(records);
    }

//    @PostMapping("/upload")
//    public Result upload(MultipartFile file) throws Exception {
//        if(file.isEmpty()){
//            return Result.error("文件为空，上传失败！");
//        }
//        System.out.println(file.getOriginalFilename());
//        //输出日志
//        log.info("上传文件：{}", file.getOriginalFilename());
//        String originalfilename = file.getOriginalFilename();
//        String dir = "src/main/java/com/example/assets";
//        String hashData = ipfsService.uploadToIpfs(dir + originalfilename);
//        System.out.println(hashData);
//        return Result.success(hashData);
//    }
}

